In [1]:
# Manipular de datos
import pandas as pd
import numpy as np

EXPLORACION DE DATOS

In [2]:
# Cargar datasets
df15 = pd.read_csv('data/bici15_cdn.csv')
In [3]:
df16 = pd.read_csv('data/bici16_cdn.csv')
In [4]:
df17 = pd.read_csv('data/bici17_cdn.csv')
In [5]:
df18 = pd.read_csv('data/bici18_cdn.csv')
In [6]:
df19 = pd.read_csv('data/bici19_cdn.csv')
In [7]:
# Las columnas son las mismas para todos
df18.columns
Out[7]:
Index(['bici_id_usuario', 'bici_Fecha_hora_retiro', 'bici_tiempo_uso',
       'bici_nombre_estacion_origen', 'bici_estacion_origen',
       'bici_nombre_estacion_destino', 'bici_estacion_destino', 'bici_sexo',
       'bici_edad'],
      dtype='object')
In [8]:
# Graficar
import matplotlib.pyplot as plt
import seaborn as sns
In [9]:
# Graficamos un histograma por año para ver cantidad de casos por edad
plt.figure(figsize=(20,10))

plt.subplot(2,2,1)
viajes15 = df15.bici_edad
viajes15.plot.hist(grid=True, bins=20, rwidth=0.9,
                   color='#2F9599')
plt.title('Recorridos 2015')

plt.subplot(2,2,2)
viajes16 = df16.bici_edad
viajes16.plot.hist(grid=True, bins=20, rwidth=0.9,
                   color='#F7DB4F')
plt.title('Recorridos 2016')

plt.subplot(2,2,3)
viajes17 = df17.bici_edad
viajes17.plot.hist(grid=True, bins=20, rwidth=0.9,
                   color='#FF847C')
plt.title('Recorridos 2017')

plt.subplot(2,2,4)
viajes18 = df18.bici_edad
viajes18.plot.hist(grid=True, bins=20, rwidth=0.9,
                   color='#FECEAB')
plt.title('Recorridos 2018')

plt.suptitle('Distribución de los viajes en bicicleta por edad. CABA 2015-2018', fontsize=15);
In [10]:
# Los grafico aparte porque no cubren todo el año
viajes19 = df19.bici_edad
viajes19.plot.hist(grid=True, bins=20, rwidth=0.9,
                   color='#607c8e', figsize=(15,5))
plt.title('Recorridos 2019')
plt.xlabel('Edad')
plt.ylabel('Frecuencia')
plt.grid(axis='y', alpha=0.3);

IDEA 1: Quiénes son los usuarios más frecuentes del servicio de bicicletas? Los jóvenes entre 25 y 35 años (distribución con sesgo a la derecha)

In [11]:
# Diferencias de uso por genero (2019)
edad = df19.bici_edad
sexo = df19.bici_sexo

plt.figure(figsize=(15,5))
plt.hist([[e for e, s in zip(edad, sexo) if s=='MASCULINO'], 
          [e for e, s in zip(edad, sexo) if s=='FEMENINO']], 
          color=['#FF847C','#ADD8E6'], bins=20, stacked=True)
plt.grid(alpha=0.3)
plt.title('Recorridos 2019 según sexo del usuario')
plt.show();
In [12]:
# Cantidad de hombres y mujeres por sexo (de aca saco la data para los graficos de torta)
df18.groupby(['bici_sexo'])['bici_edad'].count()
Out[12]:
bici_sexo
FEMENINO         807495
MASCULINO       2162906
NO INFORMADO         78
Name: bici_edad, dtype: int64
In [13]:
# Usuarios por sexo
# Plot 1
labels = ['MASCULINO', 'FEMENINO']
sizes = [391221, 112646]
colors = ['#ADD8E6', '#FF847C']
explode = (0.09, 0)  

# Plot 2
sizes2 = [537252, 178962]

# Plot 3
sizes3 = [1402028, 484136]
explode3 = (0.09, 0)  

# Plot 4
sizes4 = [2162906, 807495]
 
# Grilla de plots
plt.figure(figsize=(15,6))

plt.subplot(1,4,1)
plt.pie(sizes, explode=explode, labels=None, colors=colors,
        autopct='%1.1f%%', shadow=True, startangle=140, pctdistance = 0.5)
 
plt.legend(labels, loc=(0.22,0.1))
plt.axis('equal')
plt.title('Usuarios por sexo \n Recorridos CABA-2015', y=0.8)

plt.subplot(1,4,2)
plt.pie(sizes2, explode=explode, labels=None, colors=colors,
        autopct='%1.1f%%', shadow=True, startangle=140, pctdistance = 0.5)
plt.legend(labels, loc=(0.22,0.1))
plt.title('Usuarios por sexo \n Recorridos CABA-2016', y=0.8)
plt.axis('equal')

plt.subplot(1,4,3)
plt.pie(sizes3, explode=explode, labels=None, colors=colors,
        autopct='%1.1f%%', shadow=True, startangle=140, pctdistance = 0.5)
plt.legend(labels, loc=(0.22,0.1))
plt.title('Usuarios por sexo \n Recorridos CABA-2017', y=0.8)
plt.axis('equal')

plt.subplot(1,4,4)
plt.pie(sizes4, explode=explode, labels=None, colors=colors,
        autopct='%1.1f%%', shadow=True, startangle=140, pctdistance = 0.5)
plt.legend(labels, loc=(0.22,0.1))
plt.title('Usuarios por sexo \n Recorridos CABA-2018', y=0.8)
plt.axis('equal')
plt.show();

Los resultados por sexo indican que la mayoría de los usuarios son varones. Un resultado similar encontré en este artículo: https://medium.com/@maglionejm_15007/entendiendo-con-un-poco-m%C3%A1s-de-profundidad-el-sistema-de-movilidad-sustentable-de-la-ciudad-de-f157ea3eb345

In [14]:
from datetime import datetime
In [15]:
# Función para devolver columna de fecha y hora
def fecha_hora(df):
    '''
    Separa la indormacion de fecha y hora que en todos los df estan juntas como string
    
    Argumentos
    ----------
    
    df: el dataframe de cada año de recorridos de bicis
    '''
    
    # Se splitea la columna en dos: fecha y hora que en todos los df estan juntas como str
    fecha_hora = df['bici_Fecha_hora_retiro'].str.split(' ', expand=True)
    
    # Se almacena en un nuevo df
    fecha_hora.columns = ['Fecha','Hora']
    
    return fecha_hora
In [16]:
# Función para devolver dia de la semana
def obtener_dia_semana(fecha):
    '''
    Devuelve el dia de la semana a partir de la fecha
    
    Argumentos
    ----------
    
    fecha: columna del df, para usarla hay que mapearla porque strptime no toma series 
    '''
    return datetime.strptime(fecha,'%Y-%m-%d').weekday()
In [17]:
# Cómo utilizar ambas funciones en cualquiera de los dataframes

# 1) se almacenan las columnas de fecha y hora 
tiempo19 = fecha_hora(df19)

# 2) a partie de la Fecha se obtiene el dia de la semana mapeando la última función
dia19 = tiempo19.Fecha.map(obtener_dia_semana)
In [18]:
# Creo una copia por las dudas
df19b = df19.copy()
In [19]:
# Reemplaza numero de dia por nombre. Se le pasa cualquier de los dataframes
def nombre_dia(df):
    dia = df.replace({0:'lunes', 1:'martes', 2:'miercoles', 3:'jueves', 4:'viernes', 5:'sabado', 6:'domingo'})
    return dia
In [20]:
# Dias de la semana
df19b['dia_semana'] = nombre_dia(dia19)
In [21]:
# Hora del dia
df19b['hora_dia'] = tiempo19.Hora
In [22]:
# Funcion para clasificar el momento del dia. Se pueden cambiar los horarios si resulta arbitrario
def momento_dia(x):
    
    if (x['hora_dia']>= '07:00:00') & (x['hora_dia']<= '11:30:00'):
        return '1.Mañana'
    
    if (x['hora_dia']>='11:30:00') & (x['hora_dia'] <= '14:30:00'):
        return '2.Mediodia'
    
    if (x['hora_dia']>'14:30:00') & (x['hora_dia'] < '17:00:00'):
        return '3.Media tarde'
    
    if (x['hora_dia']>='17:00:00') & (x['hora_dia'] <= '19:00:00'):
        return '4.Tarde'
    
    if (x['hora_dia']>'19:00:00') & (x['hora_dia'] <= '21:00:00'):
        return '5.Tarde noche'
    
    if (x['hora_dia']>'21:00:00') & (x['hora_dia'] <= '24:00:00'):
        return '6.Noche'
    
    if (x['hora_dia']>'24:00:00') & (x['hora_dia'] < '07:00:00'):
        return '7.Madrugada'
In [23]:
momento_dia = df19b.apply(lambda x: momento_dia(x),1)
In [24]:
df19b['momento_dia'] = momento_dia
In [25]:
# Aca se podria ver si al patron cambia filtrando para fines o dias de semana
sexo_dia = df19b.groupby(['momento_dia','bici_sexo'])[['bici_sexo']].count()
In [26]:
sexo_dia.columns = ['Cantidad']
In [27]:
sexo_dia = sexo_dia.reset_index()
In [28]:
plt.figure(figsize=(12,5))
ax = sns.lineplot(x="momento_dia", y="Cantidad", hue='bici_sexo', data=sexo_dia.loc[sexo_dia['bici_sexo']!='NO INFORMADO'], palette = ['#FF847C','#ADD8E6'])
ax.set_facecolor("white")
plt.title('Cantidad de usuarios por sexo segun momento del recorrido. CABA-2019', y=1.05)
plt.grid()
In [29]:
# Total de estaciones de origen
len(df19b.bici_nombre_estacion_origen.unique())
Out[29]:
170
In [30]:
# Total de estaciones de destino
len(df19b.bici_nombre_estacion_destino.unique())
Out[30]:
171

GEOLOCALIZACION (para aplicar despues de configurar la red)

In [31]:
# Geolocalizacion
import geopandas as gpd
import mplleaflet
In [32]:
# Shape de estaciones descargados de DataBA
estaciones = gpd.read_file('data/estaciones_de_bicicletas.shp')
In [33]:
estaciones.NOMBRE.unique()
Out[33]:
array(['Facultad de Derecho', 'Retiro', 'Aduana', 'Plaza Roma',
       'Parque Lezama', 'Plaza Italia', 'Obelisco', 'Congreso',
       'Parque Las Heras', 'Puerto Madero - UCA', 'Tribunales',
       'Plaza Vicente López', 'Once', 'Pacífico', 'Plaza Houssay',
       'Legislatura (ex Plaza de Mayo)', 'Plaza Almagro', 'Independencia',
       'Plaza San Martín', 'Distrito Audiovisual', 'Arenales', 'Suipacha',
       'Alsina', 'Plaza Guemes', 'Juana Manso', 'Montevideo',
       'Plaza Boedo', 'Parque Centenario', 'Parque Patricios', 'Peña',
       'Catedral', 'Ingeniero Butty', 'Maipú', 'Piedras',
       'Plaza Libertad', 'Urquiza', 'Facultad de Medicina',
       'Galerías Pacífico', 'Sarmiento', 'Zoológico', 'Tucumán', 'Chile',
       'Córdoba', 'Ricardo Rojas', 'Diagonal Norte', '25 de Mayo',
       'Rivarola', 'Belgrano', 'Sánchez de Bustamante',
       'Hospital Italiano', 'Coronel Díaz', 'Julián Álvarez', 'Perón',
       'Ministro Carranza', 'Plaza Palermo Viejo', 'Ayacucho', 'Riobamba',
       'Padilla', 'Guzmán', 'Cerrito', 'Instituto Leloir',
       'Colegio Nacional Buenos Aires', 'Ministerio de Economía',
       'Esmeralda', 'Ecuador', 'Venezuela', 'Reconquista', 'Malabia',
       'Carlos Gardel', 'Yatay', 'Pasco', 'Balcarce', 'Moreno',
       'Della Paolera', 'Lavalle', 'Aguero', 'Billinghurst', 'Aráoz',
       'Bouchard', 'Quintana', 'Hospital Rivadavia', 'Misiones',
       'Suipacha y Arroyo', 'Doblas', 'Senillosa', 'Guayaquil',
       'Avelino Díaz', 'Treinta y Tres Orientales', 'Saavedra',
       'Cementerio de la Recoleta', 'Azucena Villaflor', 'Vera Peñaloza',
       'México', 'Rincón', 'Salcedo', 'Sarandí', 'Plaza Primero de Mayo',
       'Hospital Ramos Mejía', 'Aimé Painé', 'Acevedo', 'Fitz Roy',
       'Usina del Arte', 'Don Bosco', 'Julieta Lanteri', 'Humahuaca',
       'Uruguay', '9 de Julio', 'Emilio Mitre', 'Plaza Hipólito Bouchard',
       'Corrientes', 'Marcelo T. de Alvear', 'Lima', 'Gallo',
       'Facultad de Ingeniería', 'Esmeralda y Corrientes',
       '15 de Noviembre', 'Acuña de Figueroa', 'Castillo',
       'Guardia Vieja', 'Costa Rica', 'Lerma', 'Gutiérrez', 'Pueyrredón',
       'Guatemala', 'Velasco', 'Hipólito Yrigoyen', 'Armenia',
       'Ugarteche', 'Virrey Liniers', 'Las Casas', 'Pinzón', 'Cabello',
       'Ministerio de Justicia y Seguridad', 'Federico Lacroze',
       'Ruy Díaz de Guzmán', 'Rivadavia y 9 de Julio',
       'Plaza Cecilia Grierson', 'Ravignani', 'Billinghurst y Mansilla',
       'Villarroel', 'Juan Manuel Blanes', 'Paraná', 'Brasil',
       'Hospital Francés', 'Carlos Calvo', 'Macacha Guemes', 'Malba',
       'Godoy Cruz y Libertador', 'Ravignani y Guatemala',
       'Hospital Británico', 'Retiro II', 'Retiro III',
       'Hospital Garrahan', 'Nicaragua', 'Billinghurst y Valentín Gomez',
       'Azopardo y Chile', 'Oro', 'Humberto Primo', 'Hospital Alemán',
       'Solís y Alsina', 'Santos Dumont y Otero', 'Armenia y Gorriti',
       'Constitución I', 'Constitución II',
       'Parque José Evaristo Uriburu', 'Once II', 'Senillosa y Zuviría',
       'Gorriti', 'Larrea y Bartolomé Mitre', 'San Luis y Ecuador',
       'Plaza Monseñor Miguel de Andrea', 'Estados Unidos', 'Pasteur',
       'Plaza Alemania', 'Facultad de Psicología',
       'Ministerio de Educación', 'Planetario', 'José Marmol',
       'Castro y México', 'Virrey Cevallos', 'Av Pavón y 33 Orientales',
       'Catamarca', 'Talcahuano', 'Arenales y Aguero',
       'Perón y Francisco Acuña de Figueroa', 'Aranguren',
       'Pedro Echague', 'Posadas', 'Parque Rivadavia', 'Plaza Garay',
       'Juncal', 'Quintino Bocayuva', 'Estados Unidos y Boedo',
       'Hospital Sardá', 'Hospital Argerich', 'Congreso II', 'Roseti',
       'Austria y French', 'La Boca'], dtype=object)
In [34]:
# Groupby por origen y destino
origen = df19.groupby(['bici_nombre_estacion_origen'])[['bici_sexo']].count().reset_index()
destino = df19.groupby(['bici_nombre_estacion_destino'])[['bici_sexo']].count().reset_index()
In [35]:
# Usamos origen y destino para mergear con el shape de puntos
origen_map = pd.merge(estaciones,origen, left_on='NOMBRE', right_on='bici_nombre_estacion_origen')
destino_map = pd.merge(estaciones,destino, left_on='NOMBRE', right_on='bici_nombre_estacion_destino')
In [36]:
# ORIGEN
fig, ax = plt.subplots(1, figsize = (8,8))
ax.set_axis_off()
origen_map.plot(ax=ax,marker='o', color='#FF847C', markersize=origen_map['bici_sexo']/20)

mplleaflet.display(fig=ax.figure, crs=origen_map.crs)
Out[36]:
In [37]:
# DESTINO
fig, ax = plt.subplots(1, figsize = (8,8))
ax.set_axis_off()
destino_map.plot(ax=ax,marker='o', color='#2F9599', markersize=destino_map['bici_sexo']/20 )
mplleaflet.display(fig=ax.figure, crs=destino_map.crs)
Out[37]:
In [38]:
# radios censales caba
caba = gpd.read_file('data/informacion_censal_por_radio_2010.shp')
In [39]:
f, ax = plt.subplots(1,figsize=(15,8))
caba.to_crs(crs=origen_map.crs).plot(ax=ax, color='#dadada')
origen_map.plot(ax=ax,marker='o', color='#FF847C', markersize=origen_map['bici_sexo']/20 )
plt.axis('equal')
ax.set_axis_off()

OTROS GRAFICOS RELEVANTES

In [40]:
#!pip3 install plotly_express
In [41]:
# Armamos la tabla para el heatmap. Aca serian usuarios totales 
# por hora de retiro. Se puede hacer con otras variables (minutos promedio por hora de retiro) 

# Horas en eje x: Cantidad de retiros por hora segun dia de la semana
pivot1 = pd.pivot_table(df19b, values="bici_sexo",index=pd.to_datetime(df19b.bici_Fecha_hora_retiro).dt.weekday, 
               columns=pd.to_datetime(df19b.bici_Fecha_hora_retiro).dt.hour, aggfunc = 'count',fill_value=0)

# Horas en eje y
pivot2 = pd.pivot_table(df19b, values="bici_sexo",index=pd.to_datetime(df19b.bici_Fecha_hora_retiro).dt.hour, 
               columns=["dia_semana"], aggfunc = 'count',fill_value=0)
In [42]:
# Graficamos un heatmap con el pivot1
plt.figure(figsize=(15,4))
ax = sns.heatmap(pivot1, square=True, cmap=sns.diverging_palette(180, 10, as_cmap=True), linewidths=.1)
plt.setp(ax.xaxis.get_majorticklabels(), rotation=360 )

# Eje x
ax.set_xlabel("Hora del día", labelpad = 12)
labels = [item.get_text()+''+'hs' for item in ax.get_xticklabels()]
ax.set_xticklabels(labels)

# Eje y
ax.set_yticklabels(['Lunes','Martes','Miercoles','Jueves','Viernes','Sabado','Domingo'], rotation = 360)
ax.set_ylabel("")

ax.set_title('Retiro de bicicletas (en cantidad de personas) por hora . CABA-2019', y=1.05)
plt.tight_layout()
plt.show();
In [43]:
# Creamos un pivot para cada año.

#2015
pivot15 = pd.pivot_table(df15, values="bici_sexo",index=pd.to_datetime(df15.bici_Fecha_hora_retiro).dt.hour, 
               columns=pd.to_datetime(df15.bici_Fecha_hora_retiro).dt.weekday, aggfunc = 'count',fill_value=0)

#2016
pivot16 = pd.pivot_table(df16, values="bici_sexo",index=pd.to_datetime(df16.bici_Fecha_hora_retiro).dt.hour, 
               columns=pd.to_datetime(df16.bici_Fecha_hora_retiro).dt.weekday, aggfunc = 'count',fill_value=0)

#2017
pivot17 = pd.pivot_table(df17, values="bici_sexo",index=pd.to_datetime(df17.bici_Fecha_hora_retiro).dt.hour, 
               columns=pd.to_datetime(df17.bici_Fecha_hora_retiro).dt.weekday, aggfunc = 'count',fill_value=0)

#2018
pivot18 = pd.pivot_table(df18, values="bici_sexo",index=pd.to_datetime(df18.bici_Fecha_hora_retiro).dt.hour, 
               columns=pd.to_datetime(df18.bici_Fecha_hora_retiro).dt.weekday, aggfunc = 'count',fill_value=0)

#2019
pivot19 = pd.pivot_table(df19, values="bici_sexo",index=pd.to_datetime(df19.bici_Fecha_hora_retiro).dt.hour, 
               columns=pd.to_datetime(df19.bici_Fecha_hora_retiro).dt.weekday, aggfunc = 'count',fill_value=0)
In [44]:
# Graficamos un heatmap con el equivalente a pivot2. Se grafican los años anteriores
fig = plt.figure(figsize=(24,10))
ax1 = fig.add_subplot(1,4,1)
ax2 = fig.add_subplot(1,4,2)
ax3 = fig.add_subplot(1,4,3)
ax4 = fig.add_subplot(1,4,4)

#2015
sns.heatmap(pivot15, square=True, cmap=sns.diverging_palette(180, 10, as_cmap=True), linewidths=.1, ax = ax1)
plt.setp(ax1.xaxis.get_majorticklabels(), rotation=360 )
ax1.set_ylabel("Hora del día", labelpad = 12)
labels1 = [item.get_text()+''+'hs' for item in ax1.get_yticklabels()]
ax1.set_yticklabels(labels,  rotation = 0)
ax1.set_ylabel("")
ax1.set_xticklabels(['Lu','Ma','Mi','Ju','Vi','Sa','Do'], rotation = 360)
ax1.set_xlabel("")
ax1.set_title('2015', y=1.01)

#2016
sns.heatmap(pivot16, square=True, cmap=sns.diverging_palette(180, 10, as_cmap=True), linewidths=.1, ax = ax2)
plt.setp(ax1.xaxis.get_majorticklabels(), rotation=360 )
ax2.set_ylabel("Hora del día", labelpad = 12)
labels2 = [item.get_text()+''+'hs' for item in ax2.get_yticklabels()]
ax2.set_yticklabels(labels,  rotation = 0)
ax2.set_ylabel("")
ax2.set_xticklabels(['Lu','Ma','Mi','Ju','Vi','Sa','Do'], rotation = 360)
ax2.set_xlabel("")
ax2.set_title('2016', y=1.01)

#2017
sns.heatmap(pivot17, square=True, cmap=sns.diverging_palette(180, 10, as_cmap=True), linewidths=.1, ax = ax3)
plt.setp(ax3.xaxis.get_majorticklabels(), rotation=360 )
ax3.set_ylabel("Hora del día", labelpad = 12)
labels3 = [item.get_text()+''+'hs' for item in ax3.get_yticklabels()]
ax3.set_yticklabels(labels,  rotation = 0)
ax3.set_ylabel("")
ax3.set_xticklabels(['Lu','Ma','Mi','Ju','Vi','Sa','Do'], rotation = 360)
ax3.set_xlabel("")
ax3.set_title('2017', y=1.01)

#2018
sns.heatmap(pivot18, square=True, cmap=sns.diverging_palette(180, 10, as_cmap=True), linewidths=.1, ax = ax4)
plt.setp(ax4.xaxis.get_majorticklabels(), rotation=360 )
ax4.set_ylabel("Hora del día", labelpad = 12)
labels4 = [item.get_text()+''+'hs' for item in ax4.get_yticklabels()]
ax4.set_yticklabels(labels,  rotation = 0)
ax4.set_ylabel("")
ax4.set_xticklabels(['Lu','Ma','Mi','Ju','Vi','Sa','Do'], rotation = 360)
ax4.set_xlabel("")
ax4.set_title('2018', y=1.01)

fig.suptitle('Retiro de bicicletas (en cantidad de personas) por hora . CABA-2015/2018', fontsize=16) 
plt.show();
In [45]:
# Comparamos para el mismo año un meses más calurosos y más fríos

# Creamos una funcion para devolver el año y filtrar en cada dataframe por mes
def mes(df):
    mes = pd.to_datetime(df18.bici_Fecha_hora_retiro).dt.month
    df['mes'] = mes
    return df

df17b = mes(df17.copy())
df18b = mes(df18.copy())
In [46]:
# Generamos las tablas pivot para meses de calor y frio en cada año
#2017 - meses de frio
pivot17f = pd.pivot_table(data=df17b.loc[(df17b['mes']>=6) & (df17b['mes']<=8)], values="bici_sexo",index=pd.to_datetime(df17b.bici_Fecha_hora_retiro).dt.hour, 
               columns=pd.to_datetime(df17b.bici_Fecha_hora_retiro).dt.weekday, aggfunc = 'count',fill_value=0)

#2017 - meses de calor
pivot17c = pd.pivot_table(data=df17b.loc[(df17b['mes']>=9) & (df17b['mes']<=11)], values="bici_sexo",index=pd.to_datetime(df16.bici_Fecha_hora_retiro).dt.hour, 
               columns=pd.to_datetime(df16.bici_Fecha_hora_retiro).dt.weekday, aggfunc = 'count',fill_value=0)

#2018 - meses de frio
pivot18c = pd.pivot_table(data=df18b.loc[(df18b['mes']>=6) & (df18b['mes']<=8)], values="bici_sexo",index=pd.to_datetime(df17.bici_Fecha_hora_retiro).dt.hour, 
               columns=pd.to_datetime(df17.bici_Fecha_hora_retiro).dt.weekday, aggfunc = 'count',fill_value=0)

#2018 - meses de calor
pivot18f = pd.pivot_table(data=df18b.loc[(df18b['mes']>=9) & (df18b['mes']<=11)], values="bici_sexo",index=pd.to_datetime(df18.bici_Fecha_hora_retiro).dt.hour, 
               columns=pd.to_datetime(df18.bici_Fecha_hora_retiro).dt.weekday, aggfunc = 'count',fill_value=0)
In [47]:
# Graficamos un heatmap con el equivalente a pivot2. Se grafican los años anteriores
fig = plt.figure(figsize=(24,10))
ax1 = fig.add_subplot(1,4,1)
ax2 = fig.add_subplot(1,4,2)
ax3 = fig.add_subplot(1,4,3)
ax4 = fig.add_subplot(1,4,4)

#2017 - meses de frio
sns.heatmap(pivot17f, square=True, cmap='coolwarm', linewidths=.1, ax = ax1)
plt.setp(ax1.xaxis.get_majorticklabels(), rotation=360 )
ax1.set_ylabel("Hora del día", labelpad = 12)
labels1 = [item.get_text()+''+'hs' for item in ax1.get_yticklabels()]
ax1.set_yticklabels(labels1,  rotation = 0)
ax1.set_ylabel("")
ax1.set_xticklabels(['Lu','Ma','Mi','Ju','Vi','Sa','Do'], rotation = 360)
ax1.set_xlabel("")
ax1.set_title('Junio/Agosto - 2017', y=1.01)

#2017 - meses de calor
sns.heatmap(pivot17c, square=True, cmap='Spectral_r', linewidths=.1, ax = ax2)
plt.setp(ax1.xaxis.get_majorticklabels(), rotation=360 )
ax2.set_ylabel("Hora del día", labelpad = 12)
#labels2 = [item.get_text()+''+'hs' for item in ax2.get_yticklabels()]
labels2 = [str(int(i))+''+'hs' for i in pivot17c.index]
ax2.set_yticklabels(labels2,  rotation = 0)
ax2.set_ylabel("")
ax2.set_xticklabels(['Lu','Ma','Mi','Ju','Vi','Sa','Do'], rotation = 360)
ax2.set_xlabel("")
ax2.set_title('Septiembre/Noviembre - 2017', y=1.01, x=0.55)

#2018 - meses de frio
sns.heatmap(pivot18f, square=True, cmap='coolwarm', linewidths=.1, ax = ax3)
plt.setp(ax3.xaxis.get_majorticklabels(), rotation=360 )
ax3.set_ylabel("Hora del día", labelpad = 12)
labels3 = [item.get_text()+''+'hs' for item in ax3.get_yticklabels()]
ax3.set_yticklabels(labels3,  rotation = 0)
ax3.set_ylabel("")
ax3.set_xticklabels(['Lu','Ma','Mi','Ju','Vi','Sa','Do'], rotation = 360)
ax3.set_xlabel("")
ax3.set_title('Junio/Agosto - 2018', y=1.01)

#2018 - meses de calor
sns.heatmap(pivot18c, square=True, cmap='Spectral_r', linewidths=.1, ax = ax4)
plt.setp(ax4.xaxis.get_majorticklabels(), rotation=360 )
ax4.set_ylabel("Hora del día", labelpad = 12)
#labels4 = [item.get_text()+''+'hs' for item in ax4.get_yticklabels()]
labels4 = [str(int(i))+''+'hs' for i in pivot18c.index]
ax4.set_yticklabels(labels4,  rotation = 0)
ax4.set_ylabel("")
ax4.set_xticklabels(['Lu','Ma','Mi','Ju','Vi','Sa','Do'], rotation = 360)
ax4.set_xlabel("")
ax4.set_title('Septiembre/Noviembre - 2018', y=1.01, x=0.55)

fig.suptitle('Retiro de bicicletas (en cantidad de personas) por hora . Meses frios vs templados. CABA-2017/2018', fontsize=16) 
plt.show();
In [48]:
import plotly_express as px
In [49]:
# PLotly para graficos dinamicos
#https://nbviewer.jupyter.org/github/plotly/plotly_express/blob/gh-pages/walkthrough.ipynb
In [50]:
# Histograma con tiempo promedio de uso por dia de semana
px.histogram(df19b, x="dia_semana", y="bici_tiempo_uso", histfunc="avg")
In [51]:
# Creamos la variable hora en un df al azar (2018)
df18b['hora'] = pd.to_datetime(df18b.bici_Fecha_hora_retiro).dt.hour
In [52]:
# Valores agrupados para 2018 segun estacion y origen
z = df18b.groupby(['bici_nombre_estacion_origen','hora'])['bici_sexo'].count().reset_index()
In [53]:
z.head()
Out[53]:
bici_nombre_estacion_origen hora bici_sexo
0 15 de Noviembre 0 74
1 15 de Noviembre 1 57
2 15 de Noviembre 2 39
3 15 de Noviembre 3 14
4 15 de Noviembre 4 16
In [54]:
# Recuperamos del shape de estaciones la latitud y la longitud
estaciones['x'], estaciones['y']=estaciones.geometry.x, estaciones.geometry.y
In [55]:
# Joineamos los valores de latitud y longitud al dataframe "z"
v = pd.merge(z,estaciones[['NOMBRE','x','y']], right_on='NOMBRE', left_on='bici_nombre_estacion_origen')
In [56]:
# Este scatter muestra "por hora" la evolucion de la cantidad de usuarios por estacion (ubicada por lat y lon)
# Dato: conforme avanzan las horas, aumenta la cantidad de usuarios. Sobretodo a partir del mediodia

# Inquietud: clasificar color de puntos por barrio o algun otro parametro espacial (como si esta en microcentro o no)

px.scatter(v, x="x", y="y",size="bici_sexo", color="NOMBRE", hover_name="NOMBRE",
           animation_frame="hora", animation_group="NOMBRE", 
           labels=dict(x="Longitud", y="Latitud", NOMBRE = 'Estacion', hora = 'Hora', bici_sexo = 'Usuarios'))
In [57]:
# Este scatter muestra "por hora" la evolucion de los minutos promedio de uso y de la edad promedio
# Curioso: conforme avanzan las horas los promedios (puntos) se aglomeran en las edades mas altas

# Dataframe
w1 = df18b.loc[df18b['bici_sexo']!='NO INFORMADO'].groupby(['bici_nombre_estacion_origen','hora','bici_sexo'])['bici_edad','bici_tiempo_uso'].mean().reset_index()

# Plot
px.scatter(w1, x="bici_edad", y="bici_tiempo_uso", 
           animation_frame="hora", animation_group="bici_nombre_estacion_origen",
           labels=dict(bici_edad="Edad media", bici_tiempo_uso="Tiempo medio de uso", NOMBRE = 'Estacion', hora = 'Hora', bici_sexo = 'Usuarios')
           )

# Inquietud: los puntos se aglomeran (menor dispersion) en edades y tiempos medios de uso mas chicos. Presumiblementte es gente que usa la bici para ir y volver del laburo (porque son tiempos de uso mas cortos y franja etaria cercana a los treinta).
# Inquietud 2: clasificar el dataframe en "horario laboral" y "no laboral". Colorear los puntos en base a eso